# include "MyDx11.h"
# include "d3d11.h"
# include "MonitorTextureDrawAble.h"
# include "CursorTextureDrawAble.h"
# include "RemoteTextureDrawAble.h"
# include "RenderTargerTexture.h"


#define SAFE_RELEASE(p) {if(p != nullptr){(p)->Release(); p=nullptr;}}

MyDx11::MyDx11(HWND _hwnd, int _window_width, int _window_height): m_window_width(_window_width), m_window_height(_window_height)
{
	this->m_window_width = 2560;
	this->m_window_height = 1440;
	// 创建设备及交换链
	DXGI_MODE_DESC bufferDesc;
	ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC));
	bufferDesc.Width = this->m_window_width;
	bufferDesc.Height = this->m_window_height;
	bufferDesc.RefreshRate.Numerator = 0;
	bufferDesc.RefreshRate.Denominator = 1;
	bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

	DXGI_SWAP_CHAIN_DESC swapChainDesc;
	ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC));
	swapChainDesc.BufferDesc = bufferDesc;
	swapChainDesc.SampleDesc.Count = 1;
	swapChainDesc.SampleDesc.Quality = 0;
	swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	swapChainDesc.BufferCount = 1;
	swapChainDesc.OutputWindow = _hwnd;
	swapChainDesc.Windowed = TRUE;
	swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

	D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc,
		&this->m_swap_chain, &this->m_device, NULL, &this->m_context);

	// 创建呈现目标
	ID3D11Texture2D* backBuffer;
	this->m_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
	this->m_device->CreateRenderTargetView(backBuffer, nullptr, &this->m_render_target_view);
	SAFE_RELEASE(backBuffer);

	this->m_render_target_texture = new RenderTargerTexture(this->m_device, 2560, 1440);
	this->m_remote_texture_draw_able = new RemoteTextureDrawAble(this, 2560, 1440);
	//this->m_render_target_texture = new RenderTargerTexture(this->m_device, this->m_window_width, this->m_window_height);
	// 绑定呈现目标
	this->m_context->OMSetRenderTargets(1, &this->m_render_target_view, nullptr);

	// 透明
	ID3D11BlendState* blendState;
	D3D11_BLEND_DESC blendDesc = {};
	blendDesc.AlphaToCoverageEnable = FALSE;
	blendDesc.IndependentBlendEnable = FALSE;
	blendDesc.RenderTarget->BlendEnable = TRUE; // 是否开启混合
	blendDesc.RenderTarget->SrcBlend = D3D11_BLEND_SRC_ALPHA; // 将源图的 alpha 作为 src rgb 的混合因子
	blendDesc.RenderTarget->DestBlend = D3D11_BLEND_INV_SRC_ALPHA; // 将源图的 1-alpha 作为 dst rgb 的混合因子
	blendDesc.RenderTarget->BlendOp = D3D11_BLEND_OP_ADD; // 进行相加操作
	blendDesc.RenderTarget->SrcBlendAlpha = D3D11_BLEND_ONE; // 
	blendDesc.RenderTarget->DestBlendAlpha = D3D11_BLEND_ONE;
	blendDesc.RenderTarget->BlendOpAlpha = D3D11_BLEND_OP_ADD;
	blendDesc.RenderTarget->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; // 可以写入的位置
	this->m_device->CreateBlendState(&blendDesc, &blendState);
	const FLOAT BlendFactor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	this->m_context->OMSetBlendState(blendState, BlendFactor, 0xffffffff);
	SAFE_RELEASE(blendState);

	// 将视区数组绑定到管道的光栅器阶段
	D3D11_VIEWPORT viewPort;
	viewPort.TopLeftX = 0;
	viewPort.TopLeftY = 0;
	// viewPort.Width = this->m_window_width;
	// viewPort.Height = this->m_window_height;
	viewPort.MinDepth = 0.0f;
	viewPort.MaxDepth = 1.0f;
	this->m_context->RSSetViewports(1, &viewPort);
}

void MyDx11::render()
{
	const float bg_color[] = {0.0f, 0.0f, 0.0f, 0.0f};
	this->m_context->ClearRenderTargetView(this->m_render_target_view, bg_color);
	
	D3D11_VIEWPORT viewPort;
	viewPort.TopLeftX = 0;
	viewPort.TopLeftY = 0;
	// viewPort.Width = this->m_window_width;
	// viewPort.Height = this->m_window_height;
	viewPort.MinDepth = 0.0f;
	viewPort.MaxDepth = 1.0f;
	this->m_context->RSSetViewports(1, &viewPort);
	this->m_context->OMSetRenderTargets(1, &this->m_render_target_view, nullptr);
	this->m_render_target_texture->bind(this->m_context);
	this->m_swap_chain->Present(0, 0);
}

void MyDx11::copyData(ID3D11Device* _device, ID3D11DeviceContext* _context, ID3D11Texture2D* _texture, std::string _texture_name)
{
	if (_texture_name._Equal("monitor")) {
		if (this->m_monitor_texture_draw_able != nullptr) {
			this->m_monitor_texture_draw_able->copyData(_device, _context, _texture);
		}
	}
}

void MyDx11::copyData(MyDx11* _myDx11, uint8_t* _buffer, size_t _size, std::string _texture_name)
{
	if (_texture_name._Equal("monitor")) {
		if (this->m_remote_texture_draw_able != nullptr) {
			this->m_remote_texture_draw_able->copyData(this, _buffer, _size);
			this->m_remote = true;
		}
	}
}

void MyDx11::createMonitorTexture(int _src_width, int _src_height)
{
	this->removeMonitorTexture();
	this->m_monitor_texture_draw_able = new MonitorTextureDrawAble(this, _src_width, _src_height);
}

void MyDx11::removeMonitorTexture()
{
	delete this->m_monitor_texture_draw_able;
	this->m_monitor_texture_draw_able = nullptr;
}

void MyDx11::copyFromRenderTarget(uint8_t* _buffer, size_t _size)
{
	if (_buffer == nullptr) {
		return;
	}
	D3D11_MAPPED_SUBRESOURCE target_map_res;
	HRESULT hr = this->m_context->Map(this->m_render_target_texture->getCopyTexture(), 0, D3D11_MAP_READ, 0, &target_map_res);
	if (FAILED(hr)) {
		return ;
	}
	if (target_map_res.pData == nullptr) {
		this->m_context->Unmap(this->m_render_target_texture->getCopyTexture(), 0);
		return;
	}
	memcpy_s(_buffer, _size, target_map_res.pData, _size);
	this->m_context->Unmap(this->m_render_target_texture->getCopyTexture(), 0);
}

void MyDx11::renderToTexture(bool _draw_cursor)
{
	this->m_render_target_texture->renderTo(this->m_context);

	if (this->m_remote) {
		if (this->m_remote_texture_draw_able != nullptr) {
			this->m_remote_texture_draw_able->draw(this);
			// this->m_remote = false;
		}
	}
	else {
		if (this->m_monitor_texture_draw_able != nullptr) {
			this->m_monitor_texture_draw_able->draw(this);
		}

		if (_draw_cursor) {
			if (this->m_cursor_texture_draw_able != nullptr) {
				this->m_cursor_texture_draw_able->draw(this);
			}
			else {
				this->m_cursor_texture_draw_able = new CursorTextureDrawAble(this, 32, 32, 2560.0f, 1440.0f);
				this->m_cursor_texture_draw_able->draw(this);
			}
		}
	}

	this->m_render_target_texture->copyTexture(this->m_context);
}

void MyDx11::resize(int _width, int _height)
{
	this->m_window_width = _width;
	this->m_window_height = _height;
}

MyDx11::~MyDx11()
{
	delete this->m_render_target_texture;
	delete this->m_monitor_texture_draw_able;
	delete this->m_remote_texture_draw_able;
	delete this->m_cursor_texture_draw_able;
	SAFE_RELEASE(this->m_render_target_view);
	SAFE_RELEASE(this->m_context);
	SAFE_RELEASE(this->m_swap_chain);
	SAFE_RELEASE(this->m_device);
}
